home *** CD-ROM | disk | FTP | other *** search
/ Programming Sound Cards / Programming Sound Cards.iso / sound_56 / dmastp11.asm < prev    next >
Assembly Source File  |  1995-01-01  |  20KB  |  508 lines

  1. ;══════════════════════════════════════════════════════════════════════════════
  2. ; Play with Cxh/Bxh commands on SoundBlaster 16/16ASP (here 16/stereo/unsigned)
  3. ;   André Baresel (with some help from Craig Jackson)
  4. ;──────────────────────────────────────────────────────────────────────────────
  5. ; STATUS: DOES WORK ON SB16
  6. ; ■ sound crackles after a while - dounno yet what it is ...
  7. ; (somethings going wrong in CONVERT_HALF...)
  8. ;──────────────────────────────────────────────────────────────────────────────
  9. ; Requirements: 80286, SoundBlaster 16/16ASP (see BASEADDR,DMA channel,IRQ number)
  10. ; Resolutions : 16-bit / 4..44khz / stereo
  11. ; Note        : ■ We use only 8 bit stereo data and convert it while playing
  12. ;                 into stereo 16bit, look at CONVERT_HALF (at the end of this file)
  13. ;               ■ To creat a 8 bit stereo unsigned file do :
  14. ;                 "VOC2RAW TEST1.VOC /I /R"
  15. ;
  16. ; ■ DSP command 41h  ... set sample rate
  17. ; ■ DSP command D5h  ... Halt Autoinit 16 bit DMA operation
  18. ; ■ DSP command D6h  ... Continue Autoinit 16 bit DMA operation
  19. ; ■ DSP command B6h 20h ... autoinit 16 bit stereo data with no sign
  20. ;
  21.  
  22. .MODEL small
  23. .286
  24. ; CONSTANTS ───────────────────────────────────────────────────────────────────
  25.  
  26. ; SoundBlaster SETUP
  27. BASEADDR           EQU 0220h       ;SoundBlaster base address
  28. DMAchannel         EQU 5           ;SoundBlaster DMA channel
  29. IRQ7               EQU 15          ;SoundBlaster IRQ
  30.  
  31. ; PIC MASKS FOR MASK/DEMASK IRQ
  32. PICANDMASK         EQU 01111111b   ;AND PIC mask for clear IRQ
  33. PICORMASK          EQU 10000000b   ;OR PIC mask for set IRQ
  34.  
  35. ; DMA CONTROLLER REGISTERS (16bit)
  36. WRITEMASK          EQU 0D4h         ;WRITE MASK REGISTER
  37. WRITEMODE          EQU 0D6h         ;WRITE MODE REGISTER
  38. CLEARFLIPFLOP      EQU 0D8h
  39. PAGE16_CHN         EQU 08Bh         ;PAGE REGISTER FOR DMAchannel 5
  40. BASE16_CHN         EQU 0C4h         ;BASEADDRESS REGISTER DMA 5
  41. COUNT16_CHN        EQU 0C6h         ;COUNT REGISTER DMAchannel 5
  42.  
  43. ; SAMPLERATE :
  44. RATE               EQU 02AEDh       ; = 10989 Hz
  45.  
  46. ; DMA MODE
  47. WANTEDMODE         EQU 01011000b    ; singlemode, autoinit, readmode
  48.  
  49. ; DMABuffer size :
  50. DMABUFFERSIZE      EQU 8*1024
  51.  
  52. ;──────────────────────────────────────────────────────────────────────────────
  53. ; MACRO DEFINITIONs
  54. ;──────────────────────────────────────────────────────────────────────────────
  55. STARTUP                 MACRO
  56. ; MASM 5.x COMPATIBILITY
  57. __start:                mov     ax,DGROUP
  58.                         mov     ds,ax
  59.                         mov     bx,ss
  60.                         sub     bx,ax
  61.                         shl     bx,004h
  62.                         mov     ss,ax
  63.                         add     sp,bx
  64. ENDM
  65.  
  66. WAITWRITE               MACRO
  67. LOCAL                   loopWait,endloop
  68. ;          Arguments : DX = Status port (BASEADDR+0Ch)
  69. ;          Returns   : n/a
  70. ;          Destroys  : AL
  71.  
  72.                         push    cx
  73.                         xor     cx,cx           ; need that for slow SBs !
  74. loopWait:               dec     cx
  75.                         jz      endloop
  76.                         in      al,dx           ; AL = WRITE COMMAND STATUS
  77.                         or      al,al
  78.                         js      loopWait        ; Jump if bit7=1 - writing not allowed
  79. endloop:                pop     cx
  80. ENDM
  81.  
  82. WAITREAD                MACRO
  83. LOCAL                   loopWait,endloop
  84. ;          Arguments : DX = Status port   (normaly BASEADDR+0Eh)
  85. ;          Returns   : n/a
  86. ;          Destroys  : AL
  87.  
  88.                         push    cx
  89.                         xor     cx,cx           ; need that for slow SBs !
  90. loopWait:               dec     cx
  91.                         jz      endloop
  92.                         in      al,dx           ; AL = DATA AVAILABLE STATUS
  93.                         or      al,al
  94.                         jns     loopWait        ; Jump if bit7=0 - no data available
  95. endloop:                pop     cx
  96. ENDM
  97.  
  98. RESET_DSP               MACRO
  99. local                   SBthere
  100. ;          Arguments : n/a
  101. ;          Returns   : n/a
  102. ;          Destroys  : DX,AL
  103.  
  104.                         mov      dx,BASEADDR+06h
  105.                         mov      al,1
  106.                         out      dx,al          ; start DSP reset
  107.  
  108.                         in       al,dx
  109.                         in       al,dx
  110.                         in       al,dx
  111.                         in       al,dx          ; wait 3 µsec
  112.  
  113.                         xor      al,al
  114.                         out      dx,al          ; end DSP Reset
  115.  
  116.                         add      dx,08h         ; dx = DSP DATA AVAILABLE
  117.                         WAITREAD
  118.                         sub      dx,4           ; dx = DSP Read Data
  119.                         in       al,dx
  120.                         cmp      al,0aah        ; if there is a SB then it returns 0AAh
  121.                         je       SBthere
  122.                         jmp      RESET_ERROR    ; No SB - exit program
  123. SBthere:
  124. ENDM
  125. ;─── End of Macrodefinitions ──────────────────────────────────────────────────
  126.  
  127. .STACK 100h
  128.  
  129. .DATA
  130. ;──────────────────────────────────────────────────────────────────────────────
  131. ; Creat TEST1.INC with calling "VOC2RAW TEST1.VOC /I /R" or creat your own
  132. ; textfile with sampledata
  133. ;
  134. SAMPLEBUFFER LABEL BYTE
  135.     INCLUDE TEST1.INC
  136. SAMPLEBUFFEREND LABEL BYTE
  137.  
  138.     PART                db 0
  139.  
  140.     information         db 13,10,'DMASTP11.EXE - play 16bit stereo data unsigned (that does only work on a'
  141.                         db 13,10,'SB16/SB16ASP)'
  142.                         db 13,10,'Pause playing with key "p" and continue it then with any key.'
  143.                         db 13,10,'Stop playing with <ESC>.',10,'$'
  144.     memerror            db 13,10,'Not enough memory to creat the DMA buffer','$'
  145.     txtpart0            db 13,'playing part 0','$'
  146.     txtpart1            db 13,'playing part 1','$'
  147.     sberror             db 13,10,'No SoundBlaster at this BASEADDR ! PROGRAM HALTED.','$'
  148.  
  149.     OLDInterruptSEG     dw ?
  150.     OLDInterruptOFS     dw ?
  151.  
  152.     ; OFFSET AND PAGE FOR DMAC
  153.     DMAbufferOFS        dw ?
  154.     DMAbufferPage       db ?
  155.  
  156.     ; OFFSET AND SEGMENT FOR CPU ACCESS :)
  157.     DMABufferDOSOFS     dw ?
  158.     DMABufferDOSSEG     dw ?
  159.  
  160.     ; POSITION IN SAMPLEBUFFER WHILE CONVERTING
  161.     position            dw 0
  162.  
  163.     SAMPLEBUFFERLENGTH = offset SAMPLEBUFFEREND - offset SAMPLEBUFFER
  164. ;──────────────────────────────────────────────────────────────────────────────
  165. .CODE
  166.  STARTUP
  167.  
  168.            ; FIRST FREE NOT USED MEMORY :
  169.            mov     bx,ss
  170.            mov     ax,es
  171.            sub     bx,ax
  172.  
  173.            mov     ax,sp
  174.            add     ax,15
  175.  
  176.            shr     ax,4
  177.  
  178.            add     bx,ax
  179.            mov     ah,04ah
  180.            int     21h
  181.  
  182.            ; NOW ALLOCATE DMABUFFER
  183.            mov     bx,DMABUFFERSIZE*2/16       ; count of 16byte blocks for two buffers
  184.            mov     ah,48h
  185.            int     21h
  186.  
  187.            jnc     enoughmem
  188.            mov     dx,offset memerror
  189.            mov     ah,9
  190.            int     21h       ; WRITE MSG 2 SCRN THAT THERE'S NOT ENOUGH MEM
  191.            jmp     return2dos
  192. enoughmem: ; AX = segment of DMA buffer / offset = 0
  193.  
  194. ;──────────────────────────────────────────────────────────────────────────────
  195. ; calculate page and offset for 16bit DMAcontroller :
  196. ;
  197. ; segment*16+offset=20bit memory location-> upper 3 bits *2 = page
  198. ;                                           next 16 bits = offset
  199. ;                                           last 1 bit - lost in space :)
  200. ;                                           (because of word access)
  201. ;──────────────────────────────────────────────────────────────────────────────
  202.            rol     ax,4
  203.            mov     bl,al
  204.            and     bl,00eh
  205.            mov     [DMAbufferPage],bl
  206.            and     al,0f1h
  207.            ror     ax,1
  208.            mov     [DMABufferOFS],ax
  209. ;──────────────────────────────────────────────────────────────────────────────
  210. ; check for DMApage override :
  211. ; ... problem: DMA controller separates memory into 64KB pages, you can only
  212. ; transfer data is placed in one page - no page overrides are allowed
  213. ;──────────────────────────────────────────────────────────────────────────────
  214. ; To solve that :
  215. ; creat a DMA buffer with double size you want - if the first part is placed
  216. ; on a page border the second part is for sure not
  217. ;──────────────────────────────────────────────────────────────────────────────
  218.            mov     cx,DMABUFFERSIZE/2 ; we check for 128KB pages and DMABUFFERSIZE
  219.                                       ; in WORDs
  220.            neg     ax          ; ax = 65536 - ax   (bytes left to DMA page border)
  221.            cmp     ax,cx
  222.            ja      nooverride
  223.  
  224.            ; USE SECOND PART :
  225.            neg     ax               ; ax = offset first data
  226.            add     ax,cx            ; use second part
  227.            mov     [DMABufferOFS],ax
  228.            add     [DMABufferPage],2 ; 2nd part is on next page !
  229. nooverride:
  230.  
  231. ;──────────────────────────────────────────────────────────────────────────────
  232. ; now fill the  whole buffer with first words of data
  233. ; (2 times CALL CONVERT_HALF)
  234. ;
  235. ; but first - calculate the DOS SEG/OFS from the DMAPage/OFS (you know
  236. ; maybe we have to use second buffer half we don't know about ofs/seg yet)
  237.            mov     al,byte ptr [DMABufferOFS]
  238.            and     al,07h
  239.            xor     ah,ah
  240.            shl     ax,1
  241.            mov     di,ax       ; di = offset of DMAbuffer
  242.            mov     ax,[DMABufferOFS]
  243.            and     al,0f8h
  244.            mov     bl,[DMABufferPage]
  245.            shr     bl,1
  246.            or      al,bl
  247.            ror     ax,3
  248.            mov     es,ax       ; es = segment of DMABuffer
  249. ;──────────────────────────────────────────────────────────────────────────────
  250. ; save these values for later CONVERT_HALF calls
  251. ;
  252.            mov     [DMABufferDOSOFS],di
  253.            mov     [DMABufferDOSSEG],ax
  254.            xor     ax,ax
  255.  
  256.            ; DS:SI - samples in dataseg
  257.            ; ES:DI - DMABuffer
  258.  
  259.            ; fill the whole buffer with sample data
  260.            CALL    CONVERT_HALF
  261.            CALL    CONVERT_HALF
  262.  
  263.            ; NOW WE'RE READY FOR SB STUFF:
  264.  
  265.            RESET_DSP
  266.  
  267.            ; WRITE INFOMRATION TO SCREEN :
  268.            mov     dx,offset information
  269.            mov     ah,9
  270.            int     21h                  ; write program information to screen
  271.  
  272.            ; ENABLE SB SPEAKERS (for all SBs <SB16)
  273.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  274.            WAITWRITE
  275.            mov     al,0D1h                     ; AL = Enable speaker
  276.            out     dx,al                       ; Output: DSP Write Data or Command
  277.  
  278.            ; SETUP IRQ :
  279.            xor     ax,ax
  280.            mov     es,ax                       ; es to page 0 (Interrupt table)
  281.            mov     si,IRQ7*4                   ; si = position in interrupt table
  282.  
  283.            ; DISABLE IRQ (if it was enabled somehow)
  284.            in      al,021h
  285.            and     al,PICANDMASK               ; SET MASK REGISTER BIT TO DISABLE INTERRUPT
  286.            out     021h,al
  287.  
  288.            ; CHANGE POINTER IN INTERRUPT TABLE
  289.            mov     ax,es:[si]
  290.            mov     [OLDInterruptOFS],ax        ; save offset of old interupt vector for restoring
  291.            mov     ax,OFFSET OWN_IRQ
  292.            mov     es:[si],ax                  ; set offset of new interrupt routine
  293.            mov     ax,es:[si+2]
  294.            mov     [OLDInterruptSEG],ax        ; save segment of old interupt vector for restoring
  295.            mov     ax,cs
  296.            mov     es:[si+2],ax                ; set segment of new interrupt routine
  297.  
  298.            ; CHANGE PIC MASK :
  299.            in      al,021h
  300.            and     al,PICANDMASK   ; CLEAR MASK REGISTER BIT TO ENABLE INTERRUPT
  301.            out     021h,al
  302.  
  303.            mov     cx,DMABUFFERsize/2-1       ; count of words we need :)
  304. ;──────────────────────────────────────────────────────────────────────────────
  305. ; Setup DMA-controller :
  306. ;
  307. ; 1st  MASK DMA CHANNEL
  308. ;
  309.            mov     al,DMAchannel-4             ; channels 0..3 for the 2nd DMAC
  310.            add     al,4
  311.            out     WRITEMASK,al
  312. ;──────────────────────────────────────────────────────────────────────────────
  313. ; 2nd  CLEAR FLIPFLOP
  314. ;
  315.            out     CLEARFLIPFLOP,al
  316. ;──────────────────────────────────────────────────────────────────────────────
  317. ; 3rd  WRITE TRANSFER MODE
  318. ;
  319.            mov     al,WANTEDMODE
  320.            add     al,DMAchannel-4
  321.            out     WRITEMODE,al
  322. ;──────────────────────────────────────────────────────────────────────────────
  323. ; 4th  WRITE PAGE NUMBER
  324. ;
  325.            mov     al,[DMAbufferPage]
  326.            out     PAGE16_CHN,al
  327. ;──────────────────────────────────────────────────────────────────────────────
  328. ; 5th  WRITE BASEADDRESS
  329. ;
  330.            mov     ax,[DMABufferOFS]
  331.            out     BASE16_CHN,al
  332.            mov     al,ah
  333.            out     BASE16_CHN,al
  334. ;──────────────────────────────────────────────────────────────────────────────
  335. ; 6th  WRITE BUFFERLENGTH (in words)-1
  336. ;
  337.            mov     al,cl
  338.            out     COUNT16_CHN,al
  339.            mov     al,ch
  340.            out     COUNT16_CHN,al
  341. ;──────────────────────────────────────────────────────────────────────────────
  342. ; 7th  DEMASK CHANNEL
  343. ;
  344.            mov     al,DMAchannel-4
  345.            out     WRITEMASK,al
  346.  
  347. ;──────────────────────────────────────────────────────────────────────────────
  348. ; Setup SoundBlaster :
  349. ;
  350. ; 1st  SET SAMPLERATE
  351. ;
  352.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  353.            WAITWRITE
  354.            mov     al,041h                     ;AL = Set DAC Samplerate
  355.            out     dx,al
  356.            WAITWRITE
  357.            mov     cx,RATE
  358.            mov     al,ch
  359.            out     dx,al
  360.            WAITWRITE
  361.            mov     al,cl
  362.            out     dx,al
  363. ;──────────────────────────────────────────────────────────────────────────────
  364. ; 2nd  USE 16bit STEREO UNSIGNED MODE (DSPC B6h 20h)
  365. ;
  366.            WAITWRITE
  367.            mov     al,0B6h                     ;AL = DMA DAC 16bit autoinit
  368.            out     dx,al
  369.            WAITWRITE
  370.            mov     al,020h                     ;AL = stereo unsigned data
  371.            out     dx,al
  372.            mov     cx,DMABUFFERSIZE/4-1        ;half 16bit buffer
  373.            WAITWRITE
  374.            mov     al,cl                       ;AL = LOWER PART SAMPLELENGTH
  375.            out     dx,al
  376.            WAITWRITE
  377.            mov     al,ch                       ;AL = HIGHER PART SAMPLELENGTH
  378.            out     dx,al
  379.  
  380. ; TRANSFER STARTs.....NOW.... :)
  381. waitloop:  mov     ah,01                       ;AH = Check for character function
  382.            int     016h                        ;   Interrupt: Keyboard
  383.            jz      waitloop                    ; wait for a key (sound in background)
  384.  
  385.            xor     ah,ah                       ;Read character, flush keypress
  386.            int     016h                        ;   Interrupt: Keyboard
  387.            cmp     al,'p'                      ; check for pause key
  388.            je      pause                       ; ok
  389.            cmp     al,27
  390.            jne     waitloop
  391.            jmp     exit
  392. pause:     ; NOW PAUSE PLAYING (on DSPv4.04 you can also use d0h,d4h)
  393.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  394.            WAITWRITE
  395.            mov     al,0D5h
  396.            out     dx,al
  397.  
  398.            ; WAIT FOR ANY KEY
  399.            xor     ah,ah                       ;Read character, flush keypress
  400.            int     016h                        ;   Interrupt: Keyboard
  401.  
  402.            ; CONTINUE PLAYING
  403.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  404.            WAITWRITE
  405.            mov     al,0D6h
  406.            out     dx,al
  407.  
  408.            jmp     waitloop
  409.  
  410. exit:      RESET_DSP
  411.  
  412.            ; RESTORE PIC MASK
  413.            in      al,021h
  414.            or      al,PICORMASK                ;<-- SET REGISTER MASK BITS TO DISABLE
  415.            out     021h,al
  416.  
  417.            ; RESTORE IRQ :
  418.            xor     ax,ax
  419.            mov     es,ax                       ; es to page 0 (Interrupt table)
  420.            mov     si,IRQ7*4
  421.            mov     ax,[OLDInterruptOFS]
  422.            mov     es:[si],ax                  ; set old interrupt routine
  423.            mov     ax,[OLDInterruptSEG]
  424.            mov     es:[si+2],ax
  425.  
  426.            ; CLEAR KEYBUFFER
  427.            mov     ah,01
  428.            int     16h
  429.            jz      return2dos
  430.            xor     ah,ah                       ;Read character, flush keypress
  431.            int     016h                        ;   Interrupt: Keyboard
  432.  
  433.            ; TERMINATE EXE:
  434. return2dos:
  435.            mov     ax,04c00h
  436.            int     21h
  437.  
  438. ; display information if Soundblaster is not on this baseaddress
  439. RESET_ERROR:
  440.            mov     dx,offset sberror
  441.            mov     ah,9
  442.            int     21h                         ; text output
  443.            jmp     return2dos
  444.  
  445. ;──────────────────────────────────────────────────────────────────────────────
  446. ; Our own IRQ for detecting buffer half SB currently plays
  447. ; It's generated by the SoundBlaster hardware
  448. ;──────────────────────────────────────────────────────────────────────────────
  449.  
  450. OWN_IRQ:   pusha
  451.            mov     dx,BASEADDR+00Fh            ;DX = IRQ ACKNOWLEDGE 16Bit
  452.            in      al,dx
  453.            mov     ax,@data
  454.            mov     ds,ax
  455.            mov     dx,offset txtpart0
  456.            cmp     [part],0
  457.            je      notpart1
  458.            mov     dx,offset txtpart1
  459. notpart1:  mov     ah,9
  460.            int     21h             ; text output
  461.            call    CONVERT_HALF    ; fill next half...
  462.            mov     al,020h
  463.            out     020h,al                     ;ACKNOWLEDGE HARDWARE INTERRUPT
  464.            popa
  465.            IRET
  466.  
  467. ;──────────────────────────────────────────────────────────────────────────────
  468. ; Convert_half is for copying 8bit data from dataseg to dmabuffer with 16bit
  469. ; values (16bitvalue= 8bit value*256)
  470. ; one call - convert one buffer half
  471. ; next call - convert the other buffer half
  472. ; ... 2B Continued ...
  473. ; Note: no problem with stereo - it's stored like this :
  474. ; 1st byte for left,2nd for right,left,right .... etc.
  475. ; That's why we can forget about this while converting
  476. ;──────────────────────────────────────────────────────────────────────────────
  477. CONVERT_HALF:
  478.            mov     cx,DMABUFFERSIZE/2            ; half buffer size in bytes
  479.            mov     di,[DMABufferDOSOFS]
  480.            cmp     [part],0
  481.            je      not2nd
  482.            add     di,cx
  483. not2nd:    shr     cx,1                        ; count of words in half buffer
  484.            mov     ax,[DMABufferDOSSEG]
  485.            mov     es,ax
  486.            mov     si,offset samplebuffer
  487.            add     si,[position]
  488.            xor     al,al
  489. cloop:     mov     ah,ds:[si]
  490.            stosw
  491.            inc     si
  492.            cmp     si,offset samplebuffer + samplebufferlength-2
  493.            ja      samplerestart
  494.            loop    cloop
  495.            jmp     afterloop
  496. samplerestart:
  497.            ; restart sample
  498.            mov     si,offset samplebuffer
  499.            loop    cloop
  500. afterloop:
  501.            sub     si,offset samplebuffer
  502.            mov     [position],si
  503.            neg     [part]
  504.            inc     [part]      ; part = 1-part  result: 0,1,0,1,0,1,...
  505.            RET
  506.  
  507. END     __start
  508.